package main

import (
	"math/rand"
	"net"
	"os"
	"server_cluster/chat/chat_config"
	"server_cluster/chat/db"
	"server_cluster/chat/gameLogic"
	"server_cluster/chat/sharemem"
	"server_cluster/common/config"
	"server_cluster/common/logger"
	"server_cluster/common/myepoll"
	"server_cluster/common/mysock"
	"server_cluster/common/sharedb"
	"server_cluster/common/tools"
	"strconv"
	"sync"
	"time"
)

var wg sync.WaitGroup

func handleConn() {
	wg.Add(1)
	defer wg.Done()
	var timeout int64
	var result uint16
	var myshm = sharemem.Myshm
	eps := &myepoll.Eps
	mybuf := mysock.Create()
	next_time := myshm.Time_now + chat_config.USER_CHECK_TIME

	for myshm.Status == 1 || myshm.Status == 2 {
		// 超时判断 | 统一广播
		// sleep 0.2s
		// time.Sleep(2 * 100 * time.Millisecond)
		eps.Fds.Range(func(key, value interface{}) bool {
			tmpbuf := mysock.Create()
			v, ok := value.(*(myepoll.Conn))
			if ok {
				if v.Timeout < myshm.Time_now {
					// 超时
					//logger.Info("socket time out ...")
					if gameLogic.Heart(v.Socket, tmpbuf) != config.OK {
						v.Socket.Close()
						eps.Fds.Delete(key)
					}
				}
				// 统一广播 to do ...
				//
			}
			return true
		})
		if myshm.Time_now > next_time {
			next_time += chat_config.USER_CHECK_TIME
			gameLogic.ShmUserCheck()
		}
		// 2秒
		es, err := eps.Wait(2000)
		if err != nil {
			continue
		}
		for _, e := range es {
			conns := eps.GetConnByFd(e.Fd)
			if conns == nil {
				logger.Error("%d conn by fd not found.", e.Fd)
				continue
			}
			// 有消息
			result = mybuf.ReadFrom(conns.Socket)
			logger.Info("Battle read from Gate:Command %d Session=%d Code=%d,Data_length = %d Ext=%d", mybuf.Command, mybuf.Session, mybuf.Code, mybuf.Data_length, mybuf.Ext)
			if result != config.OK {
				if result == config.TIMEOUT {
					continue
				}
				logger.Warn("%d connection is broken. %s", result, conns.Socket.RemoteAddr())
				conns.Socket.Close()
				eps.Fds.Delete(e.Fd)
				continue
			}
			gameLogic.ServerAction(conns.Socket, mybuf, e.Fd)
			timeout = myshm.Time_now + config.TIME_OUT_MAX - 10
			if timeout > conns.Timeout {
				conns.Timeout = timeout
			}
			eps.Fds.Store(e.Fd, conns)
			//
		}
		//
	}
	eps.Fds.Range(func(key, value interface{}) bool {
		v, ok := value.(*(myepoll.Conn))
		if ok {
			logger.Warn("close socket %v...%s", key, v.Socket.RemoteAddr())
			v.Socket.Close()
			eps.Fds.Delete(key)
			//
		}
		return true
	})
}

func listenAccept() {
	logger.Warn("listen...")
	listener, err := net.Listen("tcp4", chat_config.CONF.GAMEHOST)
	if err != nil {
		logger.Error("%v", err)
		os.Exit(0)
	}
	eps := &myepoll.Eps

	err = eps.InitServer(chat_config.SHM_USER_MAX)
	if err != nil {
		listener.Close()
		logger.Error("%v", err)
		os.Exit(0)
	}

	myshm := sharemem.Myshm
	mybuf := mysock.Create()

	go handleConn()

	logger.Warn("Server started.Listen on %s", chat_config.CONF.GAMEHOST)
	var nfd int32
	for myshm.Status == 1 {
		// 此处会阻塞
		conn, err := listener.Accept()
		if err != nil {
			continue
		}
		rawConn, err := conn.(*net.TCPConn).SyscallConn()
		if err != nil {
			continue
		}
		rawConn.Control(func(fd uintptr) {
			nfd = int32(fd)
		})
		if myshm.Status != 1 {
			conn.Close()
			break
		}
		err = eps.AddFd(nfd, conn.(*net.TCPConn), myshm.Time_now, myepoll.CONN_TYPE_GATE, 0, 0)
		if err != nil {
			conn.Close()
			continue
		}
		gameLogic.Heart(conn, mybuf)
		logger.Warn("%s connect success %d", conn.RemoteAddr().String(), nfd)
	}
	logger.Warn("listen accept stop ...")
	listener.Close()
}

// 后端服务
func backServer() {
	defer func() {
		if err := recover(); err != nil {
			logger.Error("!!! GAME MAIN LOOP PANIC !!!")
			logger.Error("error: %v", err)
			logger.Error("stack: %v", logger.Stack())
		}
	}()
	logger.Warn("back server start...")
	myshm := sharemem.Myshm
	sync_time := myshm.Time_now
	for {
		if myshm.Time_now >= sync_time {
			// 60秒同步一次
			sync_time = myshm.Time_now + 60
			gameLogic.SyncStock()
		}
		time.Sleep(1 * time.Second)
	}
}

func main() {
	chat_config.InitConfig()
	logger.InitLogger("msg-server_"+strconv.Itoa(chat_config.CONF.SERVER_ID), chat_config.CONF.LOG_LEVEL)
	defer logger.CloseLogger()
	shmid := sharemem.Get_myshm(&(sharemem.Myshm))
	if shmid <= 0 {
		logger.Error("init myshm fail.")
		return
	}
	var myshm = sharemem.Myshm
	if myshm.Status != 0 {
		logger.Error("game service is not init.")
		return
	}
	sharemem.Init_all_user_index()
	err := db.InitDB()
	if err != nil {
		logger.Error("init db fail.", err)
		return
	}
	// logger.Info("cfg = %v",sharemem.GetConfigSkillRelationMap())
	//	mmap := sharemem.GetConfigMonsterAttrMap()
	//	logger.Info("cfg = %v",mmap[1])
	defer func() {
		myshm.Status = 0
	}()
	myshm.Status = 1
	myshm.Time_now = tools.TimeNow()
	rand.Seed(myshm.Time_now)
	go backServer()
	go listenAccept()

	logger.Warn("server start...")
	cfg_server := sharedb.Config_server{}
	if !cfg_server.Update_server_status(db.Conn, int16(chat_config.CONF.SERVER_ID), int8(1)) {
		logger.Error("update server status error.")
		return
	}
	for myshm.Status == 1 {
		time.Sleep(1 * time.Second)
		myshm.Time_now = tools.TimeNow()
		// log.Println(myshm.Time_now,tools.Time2str(myshm.Time_now))
	}
	logger.Warn("server stop...status=%d", myshm.Status)
	cfg_server.Update_server_status(db.Conn, int16(chat_config.CONF.SERVER_ID), int8(0))
	for i := 3; i >= 0; i-- {
		logger.Warn("%d", i)
		time.Sleep(1 * time.Second)
	}
	myshm.Status = 3
	logger.Warn("main wait...")
	wg.Wait()
	logger.Warn("main stop ...")
	myshm.Status = 0
	logger.Warn("server stop.")
}
